package md5

import (
	"crypto/md5"
	"encoding/hex"
	"io"
	"math"
	"os"
)

// 8KB, 大文件MD5加密分块处理
const fileChunk = 8 * 1024

// 返回一个32位md5加密后的字符串
func GetMD5Encode(data string, salt ...string) string {
	hash := md5.New()
	// 加盐
	if salt != nil {
		for _, ele := range salt {
			hash.Write([]byte(ele))
		}
	}
	hash.Write([]byte(data))
	return hex.EncodeToString(hash.Sum(nil))
}

// 返回一个16位md5加密后的字符串
func Get16MD5Encode(data string) string {
	return GetMD5Encode(data)[8:24]
}

// 小文件MD5值
func MD5File(path string) string {
	file, err := os.Open(path)
	if err != nil {
		panic("File open failed")
	}
	hash := md5.New()
	_, err = io.Copy(hash, file)
	if err != nil {
		panic("File io copy error")
	}
	return hex.EncodeToString(hash.Sum([]byte("")))
}

// 分块处理大文件MD5值
func MD5ChunkFile(path string) string {
	file, err := os.Open(path)
	if err != nil {
		panic(err.Error())
	}
	defer file.Close()

	// calculate the file size
	info, _ := file.Stat()
	filesize := info.Size()

	// 分块处理
	blocks := uint64(math.Ceil(float64(filesize) / float64(fileChunk)))

	hash := md5.New()

	for i := uint64(0); i < blocks; i++ {
		blockSize := int(math.Min(fileChunk, float64(filesize-int64(i*fileChunk))))
		buf := make([]byte, blockSize)

		_, err := file.Read(buf)
		if err != nil {
			panic(err.Error())
		}
		_, err = io.WriteString(hash, string(buf)) // append into the hash
		if err != nil {
			panic(err.Error())
		}
	}
	return hex.EncodeToString(hash.Sum(nil))
}
